using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text.Json;

namespace System.Linq.Expressions;

public static class ExpressionExtensions
{
    /// <summary>
    /// 创建一个新的 lambda 表达式，除了没有返回值外，其他都相同。
    /// </summary>
    public static Expression<Action<TIn>> StripResult<TIn, TOut>(this Expression<Func<TIn, TOut>> expression) =>
        Expression.Lambda<Action<TIn>>(expression.Body, expression.Parameters);

    /// <summary>
    /// 获取关于 <see cref="Expression{TDelegate}"/> 的信息，该表达式应只包含一个方法调用。提取并返回 <see cref="MethodInfo"/> 和参数集合。
    /// </summary>
    public static (MethodInfo Method, List<KeyValuePair<string, string>> Arguments) GetMethodCallInfo(this Expression expression)
    {
        static string ValueToString(object value) =>
            value switch
            {
                null => null,
                string text => text,
                DateTime date => date.ToString("s", CultureInfo.InvariantCulture),
                byte or sbyte or short or ushort or int or uint or long or ulong or float or double or decimal =>
                    string.Format(CultureInfo.InvariantCulture, "{0}", value),
                _ => JsonSerializer.Serialize(value),
            };

        while (expression is LambdaExpression { Body: not MethodCallExpression } lambdaExpression)
        {
            expression = lambdaExpression.Body;
        }

        var operation = (MethodCallExpression)((LambdaExpression)expression).Body;
        var methodParameters = operation.Method.GetParameters();

        var arguments = operation
            .Arguments
            .Select((argument, index) => new KeyValuePair<string, string>(
                methodParameters[index].Name,
                ValueToString(Expression.Lambda(argument).Compile().DynamicInvoke())))
            .Where(pair => pair.Value != null)
            .ToList();

        return (operation.Method, arguments);
    }
}
